#!/bin/sh

e2e_test_install() {
  PGPOOLING=pgbouncerconf

  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1 \
    --set cluster.create=false --set cluster.poolingconfig="$PGPOOLING"

  deploy_curl_pod "$CLUSTER_NAMESPACE"

  DEFAULT_PGPOOLING_CR="$(kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"  -o json \
    | jq 'del(.metadata.creationTimestamp) | del(.metadata.generation) | del(.metadata.resourceVersion) | del(.metadata.selfLink) | del(.metadata.uid)' )"

  echo_raw "$DEFAULT_PGPOOLING_CR" > "$LOG_PATH/default-pgpooling-cr.json"

  DEFAULT_PGPOOLING="$(echo "$DEFAULT_PGPOOLING_CR" \
    | jq 'del(.apiVersion) | del(.kind)' \
    | jq '.spec.pgBouncer."pgbouncer.ini" = (.spec.pgBouncer."pgbouncer.ini".pgbouncer |to_entries|map(.key + "=" + .value)|join("\n"))' )"

  echo_raw "$DEFAULT_PGPOOLING" > "$LOG_PATH/default-pgpooling.json"

  kubectl delete sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"
}

e2e_test_after_all() {
  unset PGPOOLING
}

e2e_test() {
  run_test "Check that a created pgpooling can be accessed directly through the API" check_pgpooling_directly

  run_test "Check that a created pgpooling is included in the response" check_pgpooling_in_list

  run_test "Check that a pgpooling was removed from the list of pgpoolings after its deletion" check_pgpooling_removed_from_list

  run_test "Check that a pgpooling was removed from direct access after its deletion" check_pgpooling_deletion_directly

  run_test "Pg bouncer config creation through the API" test_pgpooling_create_with_api

  run_test "Check that a pgpooling created with the API is visible" api_created_pgpooling_visible

  run_test "Pg bouncer config update with the api" test_update_pgpooling_with_api

  run_test "Check that pgpooling changes are reflected in the api" test_api_updated_pgpooling_is_visible

  run_test "Check pgpooling deletion with api" test_delete_pgpooling_with_api

  run_test "Check that pgpooling deletions with the API are reflected in the API" test_api_delete_pgpooling_is_invible

  run_test "Check that pgpooling updates with kubectl are reflected in the API" test_full_pgpooling_is_visible_in_api

  run_test "Deletion protected resources should not be deleted" test_dependency_api_error

  run_test "User with wrong credentials should not be authenticated" test_authentication_api_error
}

create_pgpooling() {
  cat "$LOG_PATH/default-pgpooling-cr.json" | kubectl apply -f -

  wait_until eval 'kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"'
}

delete_pgpooling_only() {
  echo "Deleting pgpooling if there is any"
  if kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"
  then
    echo "Pgconfig $PGPOOLING found, deleting..."

    kubectl delete sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"

    wait_until eval '! kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"'
  else
    echo "No pgpooling found"
  fi
}

check_pgpooling_in_list() {
  create_pgpooling

  if run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$PGPOOLING\") | .metadata.name" \
    | grep -q "^$PGPOOLING$"
  then
    echo "Pooling config $PGPOOLING included in json response"
    return 0
  else
    echo "Pooling config $PGPOOLING not included in json response"
    return 1
  fi
}

get_pgpooling_status() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpoolconfigs/$PGPOOLING" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}"
}

check_pgpooling_directly() {
  local HTTP_STATUS

  create_pgpooling

  HTTP_STATUS="$(get_pgpooling_status)"

  if [ "$HTTP_STATUS" -eq "200" ]
  then
    echo "Pooling config $PGPOOLING was found by the api"
    return 0
  else
    echo "Pooling config $PGPOOLING was not found by the api"
    return 1
  fi
}

check_pgpooling_removed_from_list() {
  delete_pgpooling_only

  if run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" \
    | jq -r ".[] | select ( .metadata.namespace == \"$CLUSTER_NAMESPACE\") | select ( .metadata.name == \"$PGPOOLING\") | .metadata.name" \
    | grep -q "^$PGPOOLING$"
  then
    echo "Pooling config $PGPOOLING wasn't removed from cache"
    return 1
  else
    echo "Pooling config $PGPOOLING was removed from cache"
    return 0
  fi
}

check_pgpooling_deletion_directly() {
  local HTTP_STATUS

  delete_pgpooling_only

  HTTP_STATUS="$(run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpoolconfigs/$PGPOOLING" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  if [ "$HTTP_STATUS" -eq "404" ]
  then
    echo "Pooling config $PGPOOLING was not found by the api"
    return 0
  else
    echo "Pooling config $PGPOOLING was found by the api"
    return 1
  fi
}

create_pgpooling_with_api() {
  echo "Creating pgpooling $PGPOOLING with the operator API"

  delete_pgpooling_only

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X POST -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X POST')"

    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_pgpooling_create_with_api() {
  create_pgpooling_with_api

  if wait_until eval 'kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"'
  then
    echo "Pooling config created with the operator API"
  else
    echo "Pooling config wasn't created with the API"
    return 1
  fi
}

api_created_pgpooling_visible() {
  create_pgpooling_with_api

  check_pgpooling_directly
}

update_pgpooling_parameter_with_api() {
  local HTTP_STATUS

  UPDATED_PGPOOLING="$(cat "$LOG_PATH/default-pgpooling-cr.json" \
    | jq 'del(.apiVersion) | del(.kind)' \
    | jq ".spec.pgBouncer[\"pgbouncer.ini\"].pgbouncer[\"$1\"] = $2" \
    | jq '.spec.pgBouncer."pgbouncer.ini" = (.spec.pgBouncer."pgbouncer.ini".pgbouncer|to_entries|map(.key + "=" + .value)|join("\n"))' )"

  echo_raw "$UPDATED_PGPOOLING" > "$LOG_PATH/updated-pgpooling.json"

  HTTP_STATUS="$(run_curl -r "stackgres/sgpoolconfigs" -d "$LOG_PATH/updated-pgpooling.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"
  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/updated-pgpooling.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

get_max_clien_conn(){
  MAX_CLIENT_CONN="$(kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING" -o "jsonpath={.spec.pgBouncer['pgbouncer\.ini'].pgbouncer.max_client_conn}")"
  echo "$MAX_CLIENT_CONN"
}

test_update_pgpooling_with_api() {
  create_pgpooling

  update_pgpooling_parameter_with_api 'max_client_conn' '"400"'

  if get_max_clien_conn | grep -q '^400$'
  then
    echo "Connection pooling config was updated"
  else
    echo "Connection pooling config was not updated"
    return 1
  fi
}

get_pgpooling_from_api() {
  run_curl -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpoolconfigs/$PGPOOLING" -n "$CLUSTER_NAMESPACE"
}

test_api_updated_pgpooling_is_visible() {
  update_pgpooling_parameter_with_api 'max_client_conn' '"400"'

  if get_pgpooling_from_api | jq '.spec.pgBouncer["pgbouncer.ini"]' -r \
    | grep -q 'max_client_conn = 400'
  then
    echo "Pooling config updates are being reflected in the api"
  else
    echo "Pooling config updates aren't being reflected in the api"
    return 1
  fi
}

test_full_pgpooling_is_visible_in_api() {
    cat << EOF | kubectl apply -f -
apiVersion: stackgres.io/v1
kind: SGPoolingConfig
metadata:
  name: "$PGPOOLING"
  namespace: "$CLUSTER_NAMESPACE"
spec:
  pgBouncer:
    pgbouncer.ini:
      pgbouncer:
        max_client_conn: '2000'
        default_pool_size: '50'
      databases:
        foodb:
          max_db_connections: 2000
          pool_size: 20
          dbname: 'bardb'
          reserve_pool: 5
          datestyle: ISO
      users:
        user1:
          pool_mode: transaction
          max_user_connections: 50
        user2:
          pool_mode: session
          max_user_connections: 100
EOF

  ACTUAL="$(get_pgpooling_from_api | jq '.spec.pgBouncer["pgbouncer.ini"]' -r)"
  EXPECTED="[databases]
foodb = datestyle=ISO dbname=bardb max_db_connections=2000 pool_size=20 reserve_pool=5

[users]
user1 = max_user_connections=50 pool_mode=transaction
user2 = max_user_connections=100 pool_mode=session

[pgbouncer]
application_name_add_host = 1
default_pool_size = 50
ignore_startup_parameters = extra_float_digits
max_client_conn = 2000
max_db_connections = 0
max_user_connections = 0
pool_mode = session
server_check_query = \";\""

  assert_string_equal "$ACTUAL" "$EXPECTED"
}

delete_pgpooling_with_api() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE"  -d "$LOG_PATH/default-pgpooling.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "202" ] || [ "$HTTP_STATUS" = "204" ]
  then
    echo "Request acknowledged by the operator"

  else
    local ERROR_RESPONSE
    ERROR_RESPONSE="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X PUT')"
    echo "Invalid response status $HTTP_STATUS. response: $ERROR_RESPONSE"
    return 1
  fi
}

test_delete_pgpooling_with_api() {
  create_pgpooling

  delete_pgpooling_with_api

  if wait_until eval '! kubectl get sgpoolconfigs.stackgres.io -n "$CLUSTER_NAMESPACE" "$PGPOOLING"'
  then
    echo "pgpooling was deleted"
  else
    echo "pgpooling was not deleted"
    return 1
  fi
}

test_api_delete_pgpooling_is_invible() {
  local HTTP_STATUS

  create_pgpooling

  delete_pgpooling_with_api

  if wait_until eval '[ "$(get_pgpooling_status)" = "404" ]'
  then
    echo "Pooling config removed from the API"
  else
    echo "Pooling config wasn't removed from the API"
    return 1
  fi
}

test_dependency_api_error(){
  remove_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE"
  create_or_replace_cluster "$CLUSTER_NAME" "$CLUSTER_NAMESPACE" 1

  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  assert_string_equal "204" "$HTTP_STATUS"
}

test_authentication_api_error() {
  local HTTP_STATUS

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpoolconfigs"  -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/namespaces/$CLUSTER_NAMESPACE/sgpoolconfigs/$PGPOOLING" -n "$CLUSTER_NAMESPACE" -e "-LI -o /dev/null -w %{http_code}")"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X POST -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X PUT -w %{http_code} -o /dev/null')"

  check_authentication_error

  HTTP_STATUS="$(run_curl -j "$FAKE_JWT" -r "stackgres/sgpoolconfigs" -n "$CLUSTER_NAMESPACE" -d "$LOG_PATH/default-pgpooling.json" -e '-X DELETE -w %{http_code} -o /dev/null')"

  check_authentication_error
}

check_authentication_error() {
  if [ "$HTTP_STATUS" = "401" ]
  then
    echo "Request returned expected authentication error"
    return 0
  else
    echo "Request returned unexpected response status $HTTP_STATUS instead of the expected authentication error."
    return 1
  fi
}
